package org.ym.database.hibernate.query;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.ym.tools.StringTool;

import javax.persistence.criteria.*;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;

/**
 * @author ym
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
class Filter<T> {
    public static final String MAX = "max";
    public static final String MIN = "min";
    public static final String COUNT = "count";
    public static final String AVERAGE = "average";
    public static final String SUM = "sum";
    public static final String LEFT_BRACKETS = "(";
    public static final String RIGHT_BRACKETS = ")";
    /**
     * 操作方式
     */
    private QueryOperateSymbolEnum queryOperateSymbol;
    /**
     * 字段名
     */
    private String key;
    /**
     * 值
     */
    private T value;
    /**
     * 用于between
     */
    private T endValue;

    /**
     * 获取构造hashmap的key
     * @return 显示的key
     */
    public String getDisplayKey(){
        switch (queryOperateSymbol){
            case MAX:
                return StringTool.concatStr(MAX, LEFT_BRACKETS,key, RIGHT_BRACKETS);
            case MIN:
                return StringTool.concatStr(MIN, LEFT_BRACKETS,key, RIGHT_BRACKETS);
            case COUNT:
                return StringTool.concatStr(COUNT, LEFT_BRACKETS,key, RIGHT_BRACKETS);
            case AVERAGE:
                return StringTool.concatStr(AVERAGE, LEFT_BRACKETS,key, RIGHT_BRACKETS);
            case SUM:
                return StringTool.concatStr(SUM, LEFT_BRACKETS,key, RIGHT_BRACKETS);
            default:
                return key;
        }
    }

    /**
     * 生成Predicate
     *
     * @param root            当前表
     * @param criteriaBuilder 查询
     * @return predicate
     */
    Predicate generatePredicate(Root root, CriteriaBuilder criteriaBuilder) {
        if (queryOperateSymbol == null || StringUtils.isBlank(key) || criteriaBuilder == null) {
            return null;
        }
        return generatePredicate(root.get(key), criteriaBuilder);
    }

    /**
     * 生成Predicate
     *
     * @param join            联查表
     * @param criteriaBuilder 查询
     * @return predicate
     */
    Predicate generatePredicate(Join join, CriteriaBuilder criteriaBuilder) {
        if (queryOperateSymbol == null || StringUtils.isBlank(key) || criteriaBuilder == null) {
            return null;
        }
        return generatePredicate(join.get(key), criteriaBuilder);
    }

    /**
     * 生成Predicate
     *
     * @param expression      字段名
     * @param criteriaBuilder 查询
     * @return predicate
     */
    private Predicate generatePredicate(Expression expression, CriteriaBuilder criteriaBuilder) {
        switch (queryOperateSymbol) {
            case NOT_NULL:
                return criteriaBuilder.isNotNull(expression);
            case NULL:
                return criteriaBuilder.isNull(expression);
            case EMPTY:
                return criteriaBuilder.isEmpty(expression);
            case NOT_EMPTY:
                return criteriaBuilder.isNotEmpty(expression);

        }
        if (value == null) {
            return null;
        }
        if (value.getClass().isArray() || value instanceof Collection) {
            switch (queryOperateSymbol) {
                case IN:
                    return fillIn(criteriaBuilder, value, expression);
                case NOT_IN:
                    return criteriaBuilder.not(fillIn(criteriaBuilder, value, expression));
            }
        }
        expression = expression.as(value.getClass());
        switch (queryOperateSymbol) {
            case EQUAL:
                return criteriaBuilder.equal(expression, value);
            case LIKE:
                return criteriaBuilder.like(expression, StringTool.like(value));
            case NOT_EQUAL:
                return criteriaBuilder.notEqual(expression, value);
            case LEFT_LIKE:
                return criteriaBuilder.like(expression, StringTool.leftLike(value));
            case RIGHT_LIKE:
                return criteriaBuilder.like(expression, StringTool.rightLike(value));
            case NOT_LIKE:
                return criteriaBuilder.notLike(expression, StringTool.like(value));
            case NOT_LEFT_LIKE:
                return criteriaBuilder.notLike(expression, StringTool.leftLike(value));
            case NOT_RIGHT_LIKE:
                return criteriaBuilder.notLike(expression, StringTool.rightLike(value));
        }
        if (value instanceof Date && endValue != null && endValue instanceof Date) {
            switch (queryOperateSymbol) {
                case BETWEEN:
                    return criteriaBuilder.between(expression, (Date) value, (Date) endValue);
            }
        }
        if (value instanceof Number) {
            switch (queryOperateSymbol) {
                case GREATER:
                    return criteriaBuilder.gt(expression, (Number) value);
                case GREATER_EQUAL:
                    return criteriaBuilder.ge(expression, (Number) value);
                case LESS:
                    return criteriaBuilder.lt(expression, (Number) value);
                case LESS_EQUAL:
                    return criteriaBuilder.le(expression, (Number) value);
                case IN:
                    break;
                case NOT_IN:
                    break;
                case BETWEEN:
                    if (endValue != null) {
                        return criteriaBuilder.and(criteriaBuilder.ge(expression, (Number) value), criteriaBuilder.le(expression, (Number) endValue));
                    } else {
                        return null;
                    }
                case COUNT_GREATER:
                    return criteriaBuilder.gt(criteriaBuilder.count(expression),(Number)value);
                case COUNT_GREATER_EQUALS:
                    return criteriaBuilder.ge(criteriaBuilder.count(expression),(Number)value);
                case COUNT_LESS:
                    return criteriaBuilder.lt(criteriaBuilder.count(expression),(Number)value);
                case COUNT_LESS_EQUAL:
                    return criteriaBuilder.le(criteriaBuilder.count(expression),(Number)value);
                case MAX_GREATER:
                    return criteriaBuilder.gt(criteriaBuilder.max(expression),(Number)value);
                case MAX_GREATER_EQUALS:
                    return criteriaBuilder.ge(criteriaBuilder.max(expression),(Number)value);
                case MAX_LESS:
                    return criteriaBuilder.lt(criteriaBuilder.max(expression),(Number)value);
                case MAX_LESS_EQUAL:
                    return criteriaBuilder.le(criteriaBuilder.max(expression),(Number)value);
                case MIN_GREATER:
                    return criteriaBuilder.gt(criteriaBuilder.min(expression),(Number)value);
                case MIN_GREATER_EQUALS:
                    return criteriaBuilder.ge(criteriaBuilder.min(expression),(Number)value);
                case MIN_LESS:
                    return criteriaBuilder.lt(criteriaBuilder.min(expression),(Number)value);
                case MIN_LESS_EQUAL:
                    return criteriaBuilder.le(criteriaBuilder.min(expression),(Number)value);
                case AVERAGE_GREATER:
                    return criteriaBuilder.gt(criteriaBuilder.avg(expression),(Number)value);
                case AVERAGE_GREATER_EQUALS:
                    return criteriaBuilder.ge(criteriaBuilder.avg(expression),(Number)value);
                case AVERAGE_LESS:
                    return criteriaBuilder.lt(criteriaBuilder.avg(expression),(Number)value);
                case AVERAGE_LESS_EQUAL:
                    return criteriaBuilder.le(criteriaBuilder.avg(expression),(Number)value);
                case SUM_GREATER:
                    return criteriaBuilder.gt(criteriaBuilder.sum(expression),(Number)value);
                case SUM_GREATER_EQUALS:
                    return criteriaBuilder.ge(criteriaBuilder.sum(expression),(Number)value);
                case SUM_LESS:
                    return criteriaBuilder.lt(criteriaBuilder.sum(expression),(Number)value);
                case SUM_LESS_EQUAL:
                    return criteriaBuilder.le(criteriaBuilder.sum(expression),(Number)value);
            }
        }
        return null;
    }

    /**
     * 填充 in数据
     *
     * @param criteriaBuilder 对象
     * @param value           值
     * @param expression      表达式
     * @return in
     */
    private CriteriaBuilder.In fillIn(CriteriaBuilder criteriaBuilder, Object value, Expression expression) {
        CriteriaBuilder.In in = criteriaBuilder.in(expression);
        Iterator iterator = null;
        if (value.getClass().isArray()) {
            int length = Array.getLength(value);
            for (int i = 0; i < length; i++) {
                in.value(Array.get(value, i));
            }
        } else {
            iterator = ((Collection) value).iterator();
            while (iterator.hasNext()) {
                in.value(iterator.next());
            }
        }
        return in;
    }

    /**
     * 生成Expression
     *
     * @param root            当前表
     * @param criteriaBuilder 查询
     * @return predicate
     */
    Expression generateExpression(Root root, CriteriaBuilder criteriaBuilder) {
        if (queryOperateSymbol == null || StringUtils.isBlank(key) || criteriaBuilder == null) {
            return null;
        }
        return generateExpression(root.get(key), criteriaBuilder);
    }

    /**
     * 生成Expression
     *
     * @param join            联查表
     * @param criteriaBuilder 查询
     * @return predicate
     */
    Expression generateExpression(Join join, CriteriaBuilder criteriaBuilder) {
        if (queryOperateSymbol == null || StringUtils.isBlank(key) || criteriaBuilder == null) {
            return null;
        }
        return generateExpression(join.get(key), criteriaBuilder);
    }

    /**
     * 生成Expression
     *
     * @param expression      字段名
     * @param criteriaBuilder 查询
     * @return predicate
     */
    private Expression generateExpression(Expression expression, CriteriaBuilder criteriaBuilder) {
        switch (queryOperateSymbol) {
            case COUNT:
                return criteriaBuilder.count(expression);
            case SUM:
                return criteriaBuilder.sum(expression);
            case MAX:
                return criteriaBuilder.max(expression);
            case MIN:
                return criteriaBuilder.min(expression);
            case AVERAGE:
                return criteriaBuilder.avg(expression);
            default:
                return expression;
        }
    }
}
